#include "data.h"
#include "types.h"
#include <cstdint>
#include <memory>
#include <rapidjson/document.h>

PetParams g_pets;
ShowOptions sos;
DataParam g_dp;
price_t g_hop = 12;
Sender sender;
ActionList g_al;

void
to_my_lines(Trade::Lines& tl, Lines& l) noexcept
{
  l.nup = tl.nup;
  l.ndn = tl.ndn;
  l.u1 = tl.hp_up_1;
  l.u2 = tl.hp_up_2;
  l.d1 = tl.hp_dn_1;
  l.d2 = tl.hp_dn_2;
}

time_t
DataParam::get_time_by_idx(const int64_t idx) const noexcept
{
  if (!tick || idx < 0 || idx >= (int64_t)(tick_size)) {
    return (0);
  }

  return time_t(tick[idx].ftime);
}

price_t
DataParam::get_price_by_idx(const int64_t idx) const noexcept
{
  if (!tick || idx < 0 || idx >= (int64_t)(tick_size)) {
    return (0);
  }
  return tick[idx].price;
}

int
DataParam::set_pos(const int64_t pos) noexcept
{
  if ((pos == g_dp.m_posIndex) || (pos < g_dp.g_start_idx) ||
      (pos >= int64_t(g_dp.tick_size))) {
    return -1;
  } else {
    g_dp.m_posIndex = pos;
    return 0;
  }
}

int
DataParam::move_pos_delta(const int delta) noexcept
{
  int64_t pos = g_dp.m_posIndex + delta;
  return set_pos(pos);
}

int
DataParam::time_2_x(const time_t ts) const noexcept
{
  time_t t0 = get_time_by_idx(m_posIndex);
  if (!t0 || !ts)
    return (-100000);

  // printf("TTT %ld %ld %ld\n", ts, t0, ts - t0);
  return (ts - t0) / g_dp.m_frequence;
}

int
DataParam::pos_2_x(const int64_t idx) const noexcept
{
  time_t ts = get_time_by_idx(idx);
  time_t t0 = get_time_by_idx(m_posIndex);
  if (!t0 || !ts)
    return (-100000);

  return (ts - t0) / g_dp.m_frequence;
}

int
set_params_data() noexcept
{
  auto vo_ptr = Trade::get_vo_by_index(g_dp.vo, g_dp.vo_size, g_dp.m_posIndex);
  if (vo_ptr) {
    // vo_ptr->send_important_dirs();
    to_my_lines(vo_ptr->lines, g_dp.lines);
    g_dp.t1 = vo_ptr->nft.t1;
    g_dp.t2 = vo_ptr->nft.t2;
    // g_dirs.set_dirs(vo_ptr);

    return 0;
  }
  return -1;
}

int
parse_data(const char* buf)
{
  rapidjson::Document doc;
  if (doc.Parse(buf).HasParseError()) {
    return -1;
  }

  if (doc.HasMember("t")) {
    int type = doc["t"].GetInt();
    int64_t pos = 0;

    if (doc.HasMember("i"))
      pos = doc["i"].GetInt64();
    if (type == 0) {
      if ((pos == g_dp.m_posIndex) || (pos < g_dp.g_start_idx) ||
          (pos >= int64_t(g_dp.tick_size))) {
        pos = 0; // wrong or ignore
      } else {
        if (auto p = Trade::get_vo_by_index(g_dp.vo, g_dp.vo_size, pos);
            p != nullptr) {
          g_dp.t1 = p->nft.t1;
          g_dp.t2 = p->nft.t2;
          g_dp.m_posIndex = pos;
          // g_dirs.set_dirs(p);
        } else {
          pos = 0; // wrong data
        }
      }
      emit sender.sig_received_position(pos);
    } else if (type >= 11 && type <= 13) {
      Pet* p = g_pets.pet + (type - 11);
      p->tm = time_t(pos);
      if (doc.HasMember("d"))
        p->dir = (dir_t)(doc["d"].GetInt());
      if (doc.HasMember("t1"))
        p->t1 = (time_t)(doc["t1"].GetInt64());
      if (doc.HasMember("t2"))
        p->t2 = (time_t)(doc["t2"].GetInt64());
      if (doc.HasMember("p"))
        p->hp = doc["p"].GetFloat();

      emit sender.sig_set_pet_dir_time(type - 11);
    } else if (type == 14) {
      if (doc.HasMember("nup"))
        g_pets.mickey_lines.nup = doc["nup"].GetInt();
      if (doc.HasMember("ndn"))
        g_pets.mickey_lines.ndn = doc["ndn"].GetInt();
      if (doc.HasMember("u1"))
        g_pets.mickey_lines.u1 = doc["u1"].GetDouble();
      if (doc.HasMember("u2"))
        g_pets.mickey_lines.u2 = doc["u2"].GetDouble();
      if (doc.HasMember("d1"))
        g_pets.mickey_lines.d1 = doc["d1"].GetDouble();
      if (doc.HasMember("d2"))
        g_pets.mickey_lines.d2 = doc["d2"].GetDouble();
      if (doc.HasMember("eu"))
        g_pets.eu = doc["eu"].GetDouble();
      if (doc.HasMember("ed"))
        g_pets.ed = doc["ed"].GetDouble();

      emit sender.sig_mickey_lines();
    } else if (type == 15) {
      if (pos == 0) {
        g_al.clear();
        emit sender.sig_clear_action();
      } else {
        static const char* aname[] = {
          "Close", "Open", "Watch", "Run", "Stop"
        };
        static const char* pname[] = {
          "mock", "tom", "jerry", "mickey", "stg"
        };
        const dir_t d = dir_t(doc["d"].GetInt());
        if (d) {
          // i, d, p, v, a, tm, w
          auto action = std::make_shared<Action>();
          action->idx = pos;
          action->d = d;
          action->hpx = doc["p"].GetFloat();
          action->tm = time_t(doc["tm"].GetInt64());
          action->vol = doc["v"].GetInt();
          action->action = doc["a"].GetInt();
          action->who = doc["w"].GetInt();
          strcpy(action->name, pname[action->who]);
          strcpy(action->act, aname[action->action]);

          if (g_al.size() > 39) {
            // g_al.erase(g_al.rbegin().base());
            auto i = g_al.end();
            --i;
            g_al.erase(i);
          }
          g_al.push_front(action);
          emit sender.sig_received_action(action);
        }
      }
    } if (type == 16) {
      int ret = doc["r"].GetInt();
      if (!ret)
        g_dp.reverse_sapace = -1;
      else
        g_dp.reverse_sapace = doc["rd"].GetFloat();
      emit sender.sig_received_reverse_sapce();
    }
  }
  return (0);
}

ActionPtr
get_action_in_list(const int idx) noexcept
{
  int i = 0;
  for (auto it = g_al.begin(); it != g_al.end(); ++it, ++i) {
    if (idx == i) {
      return *it;
    }
  }
  return nullptr;
}
